/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package com.xiashitech.agent.instrumentation.dubbo.inst.lt27;

import com.alibaba.dubbo.remoting.exchange.Request;
import com.alibaba.dubbo.remoting.exchange.Response;
import com.xiashitech.agent.cache.status.CacheStatus;
import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.dto.UserRequest;
import com.xiashitech.agent.instrumentation.bridge.Java8Bridge;
import com.xiashitech.agent.instrumentation.dubbo.XSDubboLt27Handler;
import com.xiashitech.agent.instrumentation.helper.calldepth.XSDubboLt27AbstractPeerReceivedCallDepth;
import com.xiashitech.agent.instrumentation.utils.BaggageUtil;
import com.xiashitech.agent.utils.DurationPrinter;
import io.opentelemetry.javaagent.bootstrap.CallDepth;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.shaded.io.opentelemetry.context.Scope;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

import java.util.Optional;

import static com.xiashitech.agent.instrumentation.dubbo.XSDubboSingletons.dubboInstrumenter;
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static net.bytebuddy.matcher.ElementMatchers.*;

public class AbstractPeerReceivedInstrumentation implements TypeInstrumentation {
    // private static final Logger logger = Logger.getLogger(AbstractPeerReceivedInstrumentation.class.getName());

    @Override
    public ElementMatcher<TypeDescription> typeMatcher() {
        return named("com.alibaba.dubbo.remoting.transport.AbstractPeer");
    }

    @Override
    public void transform(TypeTransformer transformer) {
        //System.out.println("###ppp1");
        transformer.applyAdviceToMethod(
                isMethod()
                        .and(named("received"))
                        .and(takesArgument(0, named("com.alibaba.dubbo.remoting.Channel")))
                        .and(takesArgument(1, named("java.lang.Object")))
                        .and(takesArguments(2))
                        .and(isPublic()),
                AbstractPeerReceivedInstrumentation.class.getName() + "$ReceivedAdvice");
       // System.out.println("###ppp2");
        // logger.log(XSLevel.DEBUG,"###debug 002 transform");
    }

    @SuppressWarnings("unused")
    public static class ReceivedAdvice {

        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void onEnter(
                @Advice.Argument(value = 1, readOnly = false) Object request,
                @Advice.This Object servletInvocableHandlerMethod,
                @Advice.Local("otelContext") Context context,
                @Advice.Local("otelCallDepth") CallDepth callDepth,
                @Advice.Local("otelScope") Scope scope,
                @Advice.Local("user") UserRequest userRequest
        ) {
            DurationPrinter durationPrinter = new DurationPrinter("AbstractPeerReceivedInstrumentation");

            try {


                if (CacheStatus.cannotRun()) {
                    System.out.println("###Cache not run  excute ");
                    return;
                }
           /* Java8Bridge.currentForBaggage().toBuilder()
                    .put("xs.userId","session_user_id(java.lang.Integer):%20140515")
                    .build().makeCurrent();*/
                System.out.println("###ppp3-0 Received Request:" + request.toString());
                // userRequest = UserRequest.builder().build();
                callDepth = CallDepth.forClass(XSDubboLt27AbstractPeerReceivedCallDepth.class);
                System.out.println("###ppp3-1");

                if (callDepth.getAndIncrement() > 0) {
                    System.out.println("###callDepth001  excute:" + callDepth.decrementAndGet());
                    return;
                }
                //scope = context.makeCurrent();
                Optional<UserRequest> userRequestOptional = Java8Bridge.executeRuleForUserInfoMatchRule(request);
                if (userRequestOptional.isPresent()) {
                    userRequest = userRequestOptional.get();
                } else {
                    userRequest = UserRequest.builder().build();
                }
                //userRequest = UserRequest.builder().build();
                userRequest.setAdviceObject(servletInvocableHandlerMethod);
                userRequest.setRequest(request);
                Context parentContext = currentContext();
              if (dubboInstrumenter().shouldStart(parentContext, userRequest)) {
                  parentContext = BaggageUtil.syncBaggage().build().storeInContext(parentContext);
                  context = dubboInstrumenter().start(parentContext, userRequest);
                    if (request instanceof Response) {
                        System.out.println("###ppp3-1-1");
                        System.out.println("###received001 resquest change to response  start:" + request.toString());
                        request = XSDubboLt27Handler.handle(userRequest, request);
                        System.out.println("###received001 resquest change to response  finished:" + request.toString());
                    } else if (request instanceof Request) {
                        System.out.println("###resquest start :" + request.toString());
                        Optional initOptional = XSDubboLt27Handler.init(userRequest, request);
                        if (!initOptional.isPresent()) {
                            System.out.println("###ppp3-1-2");
                            return;
                        }
                        System.out.println("###received002 resquest init finished  :" + request.toString());
                    } else {
                        System.out.println("###received003 request: unknow request");
                    }
                    System.out.println("###ppp4");
                  context = BaggageUtil.syncBaggage().build().storeInContext(context);
                  scope = context.makeCurrent();
              }
            } catch (Throwable e) {
                e.printStackTrace();
                StartConfig.throwableStackPrinter(e);
            }
            durationPrinter.printElapseDuration("ReceivedAdvice b");
        }

        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
        public static void onExit(
                @Advice.Argument(1) Object request,
                @Advice.Thrown Throwable throwable,
                @Advice.Local("otelContext") Context context,
                @Advice.Local("otelCallDepth") CallDepth callDepth,
                @Advice.Local("otelScope") Scope scope,
                @Advice.Local("user") UserRequest userRequest

        ) {
            DurationPrinter durationPrinter = new DurationPrinter("AbstractPeerReceivedInstrumentation");
            try {
                System.out.println("###Received exit 001");
                if (CacheStatus.cannotRun()) {
                    System.out.println("###Cache cannot run  001");
                    return;
                }
                if (callDepth.decrementAndGet() > 0) {
                    System.out.println("###callDepth002  excute:" + callDepth.decrementAndGet());
                    return;
                }
                if (scope == null) {
                    System.out.println("###ppp5-0");
                    return;
                }
                scope.close();
                dubboInstrumenter().end(context, userRequest, null, throwable);
                System.out.println("###ppp5");
            } catch (Throwable e) {
                e.printStackTrace();
                StartConfig.throwableStackPrinter(e);
            }
            durationPrinter.printElapseDuration("ReceivedAdvice e");

        }
    }
}

